#pragma once

#include "Mutex.hpp"
#include "ThreadMod.hpp"
#include "cond.hpp"
#include "Log.hpp"
#include <queue>
#include <vector>


namespace ThreadPoolModule
{
    //引入模块
    using namespace MutexModule;
    using namespace ThreadModule;
    using namespace CondModule;
    using namespace LogModule;

    //默认创建线程数量
    const int defaultnum = 5;

    template<typename T>
    class ThreadPool
    {

    private:

        void WakeUpAllThread()
        {
            //这里可能被多个线程同时访问，需要加锁保护
            MutexProtect mp(&_mutex);
            if(_sleepnumber<0)
                return;
            _cond.BroadCast();
            LOG(INFO)<<"所有线程被唤醒";
        }

        void WakeUpSignalThread()
        {
            // MutexProtect mp(&_mutex);
            // if(_sleepnumber<0)
            //     return;
            _cond.Signal();
            LOG(INFO)<<"单个线程被唤醒";
        }
        ThreadPool(int num = defaultnum)
            :_num(num),
             _isrunning(false)
             _sleepnumber(0)
        {
            for(int i=0;i<num;i++)
            {
                _threadpool.emplace_back(
                    [this](){
                        handleTask();
                    }
                );
            }
        }

        void Start()
        {
            if(_isrunning)
                return;
            _isrunning=true;
            for(auto &thread : _threadpool)
            {
                thread.start();
                LOG(DEBUG)<<thread.GetName()<<"线程被启动";
            }
        }

        ThreadPool(const ThreadPool<T>&)=delete;
        ThreadPool<T>&operator=(const ThreadPool<T>&)=delete;

    public:
        //为什么要定义成静态成员函数？因为如果定义为类的成员函数，如果我们想要访问它
        //就必须先实例化一个对象，但是为了实现单例模式，我们是不允许有构造函数和拷贝构造函数的
        //这不就矛盾了吗？所以我们需要将这个成员函数设置为静态的，这样通过类名就可以完成声明对象从而只通过
        //调用函数 仅实例化出一个对象

        //但是这样是线程不安全的，需要加锁，锁也必须是静态的，静态成员函数没有this指针，不能访问成员属性
        //所以为了保证线程安全，必须单独为这个成员函数设计一个静态的锁
        static ThreadPool<T>* GetInstance()
        {
            //但这样一次涌入大量的线程来申请锁会导致有一个线程在申请锁的过程中，其他的线程只能阻塞，
            //当一个线程申请锁后，释放了锁，其他的线程又会来申请锁，此时即使申请锁成功也是无意义的，
            //不会再实例化对象了，这样效率太低了
            //为了提高效率，我们可以在外部在设置一层判断，这样即使后面再调用此函数也是满足条件后再申请锁
            //而不是先申请锁再判断是否满足条件
            if(_instance==nullptr)
            {
                MutexProtect mp(&_lock);

                LOG(DEBUG)<<"获取单例";
                if(_instance==nullptr)
                {
                    LOG(DEBUG)<<"首次使用单例模式实例化对象,创建对象中";
                    _instance=new ThreadPool<T>();
                    _instance->Start();
                }
            }
            return _instance;

        }

        //线程结束
        void Stop()
        {
            if(!_isrunning)
                return;
            _isrunning=false;
            // for(auto &thread : _threadpool)
            // {
            //     //保证调用join能成功
            //     thread.setNotRunning();
            // }

            //调用此函数，为了防止有线程休眠而一直无法进行handleTask的条件判断，导致线程一直阻塞
            WakeUpAllThread();
        }

        //线程join
        void Join()
        {
            for(auto &thread :_threadpool)
            {
                LOG(DEBUG)<<"thread._isrunning:"<<_isrunning;
                //这里的thread的_isrunning和模板传入的是Thread的是不一样的，两边是不同的状态，所以会导致
                //_isrunning为false但是输出了线程正在运行，无法等待
                thread.join();
            }
        }

        void handleTask()
        {
            char name[128];
            pthread_getname_np(pthread_self(),name,sizeof(name));

            while(true)
            {
                T t;
                {
                    MutexProtect mp(&_mutex);
                    //使用while循环一直判断队列是否为空
                    while(_taskqueue.empty()&&_isrunning)
                    {
                        _sleepnumber++;
                        _cond.Wait(_mutex);
                        _sleepnumber--;
                    }
                    if(_taskqueue.empty()&&!_isrunning)
                    {
                        LOG(INFO)<<name<<"，线程退出了，队列为空&&没有运行的线程";
                        break;
                    }
                    t = _taskqueue.front();
                    _taskqueue.pop();
                }
                t();//不需要被保护，此时已经是线程私有的了,不能放在临界区内
            }

        }

        bool EnterQueue(const T& in)
        {
            if(_isrunning)
            {
                MutexProtect mp(&_mutex);
                _taskqueue.push(in);

                if(_taskqueue.size()==_sleepnumber)
                    WakeUpSignalThread();
                return true;
            }
            return false;
        }

        ~ThreadPool(){}
        
    private:
        //下面的变量是一个线程池里单个线程的状态
        std::vector<Thread> _threadpool;
        int _num;//线程创建的数量
        Cond _cond;
        Mutex _mutex;
        std::queue<T> _taskqueue;//处理任务

        bool _isrunning;//线程是否在运行
        int _sleepnumber;//休眠的线程的数量

        static ThreadPool<T>* _instance;
        static Mutex _lock;
    };

    //初始化静态指针和锁
    template<typename T>
    ThreadPool<T>* ThreadPool<T>::_instance=nullptr;

    template<typename T>
    Mutex ThreadPool<T>::_lock;
    
}

